#! /usr/bin/env python
# coding=utf-8
# ******************************************************************************
# Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
# licensed under the Mulan PSL v2.
# You can use this software according to the terms and conditions of the Mulan PSL v2.
# You may obtain a copy of Mulan PSL v2 at:
#     http://license.coscl.org.cn/MulanPSL2
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
# PURPOSE.
# See the Mulan PSL v2 for more details.
# Author: senlin
# Create: 2021-06-20
# ******************************************************************************/
"""
This is a simple script to query that contact person for specific package
"""
import sys
sys.path.append('/home/oect')
import argparse
import os
import re
import shutil
import shlex
import subprocess
import urllib.request
import yaml
import csv
import codecs
from datetime import time
from genericpath import isfile
from fake_useragent import UserAgent
from src.libs.executecmd import ExecuteCmd
from src.libs.logger import logger
from src.config import constant
from src.libs.base import http


class OpenEulerCommunityRepoInfoQuery(object):
    """
    get sig maintainer and contributor of package
    """
    def __init__(self, branch='master'):
        """
        @description :
        -----------
        @param :
        -----------
        @returns :
        -----------
        """
        self.branch = branch
        self.signames = []
        self.sigs_yaml_content = dict()
        self.get_local_sigs_yaml() # 读取本地的sigs.yaml内容
        self.exps = dict()
        with open(constant.LOCAL_REPO_EXCEP, encoding='utf-8') as f:
            self.exps = yaml.safe_load(f)
        
    
    def get_local_sigs_yaml(self):
        """
        get the content of local sigs.yaml 

        returns:
            sigs: content of sigs.yaml
        """
        sigs = dict()
        sigs_dir = os.listdir(constant.LOCAL_SIGS)
        logger.info(sigs_dir)
        for sig_dir in sigs_dir:
            logger.info(f"current sig_dir: {sig_dir}")
            if sig_dir == 'TC':
                continue
            self.signames.append(sig_dir)
            sigs[sig_dir] = []
            
            if isfile(os.path.join(constant.LOCAL_SIGS, sig_dir)):
                logger.info(f"current sig_dir is file: {sig_dir}")
                continue
            sig_repos_dir = os.path.join(constant.LOCAL_SIGS, sig_dir, 'src-openeuler') 
            # community/tree/master/sig/Application/src-openeuler/
            try:
                src_oe_pkgs_dir = os.listdir(sig_repos_dir)
            except FileNotFoundError as err:
                logger.warning(f"{sig_dir} has no src-openeuler/repo")
                continue

            for src_oe_prepos in src_oe_pkgs_dir:
                repos_yamls = os.listdir(os.path.join(sig_repos_dir, src_oe_prepos))
                repo_names = [repo_yaml.replace('.yaml', '') for repo_yaml in repos_yamls]
                sigs[sig_dir].extend(repo_names)

        self.sigs_yaml_content = sigs
            

    def get_local_sigs_maintainers_yaml(self, sig_name):
        """
        get the content of local sigs/OWNER 

        returns:
            sigs: content of sigs/OWNER
        """
        sigs_OWNERS_yaml_content = None
        if sig_name == 'NA':
            return sigs_OWNERS_yaml_content

        local_sigs_OWNERS_path = constant.LOCAL_SIGS_OWNERS.format(signame = sig_name)
        try:
            with open(local_sigs_OWNERS_path, encoding='utf-8') as f:
                sigs_OWNERS_yaml_content = yaml.safe_load(f)
        except FileNotFoundError as err:
            logger.warning(f"No {local_sigs_OWNERS_path} in {sig_name}")

        return sigs_OWNERS_yaml_content


    def get_package_repo_name(self, srpm_name):
        """
        get the real package repo name on gitee

        args:
            srpm_name: the name of the srpm
        returns:
            package: the name of repository on https://gitee.com/src-openeuler/ or https://build.openeuler.org
        """
        package_name = srpm_name
        spec_name = srpm_name
        # with open(constant.LOCAL_REPO_EXCEP, encoding='utf-8') as f:
        #     exps = yaml.safe_load(f)
        #     for key, value in exps.items():
        #         if srpm_name == value.get("srpm"):
        #             package_name = key
        #             spec_name = value.get("spec")
        #             logger.info(f"real package:{package_name}")
        #             break
        return package_name, spec_name
    
    def get_gitee_repo_related_name(self, name_type, name):
        """
        get the real package repo name on gitee

        args:
            name_type: the name of the packsge/srpm/spec
            name: the value of name
        returns:
            related names: packsge/srpm/spec
        """

        spec_name = name
        gitee_repo_name = name
        srpm_name = name
        for key, value in self.exps.items():
            if name_type == 'spec':
                if value.get("spec") == name:
                    gitee_repo_name = key
                    srpm_name = value.get("srpm")
                    break
            if name_type == 'srpm':
                if value.get("srpm") == name:
                    gitee_repo_name = key
                    spec_name = value.get("spec")
                    break
            if name_type == 'gitee_repo':
                if key == name:
                    spec_name = value.get("spec")
                    srpm_name = value.get("srpm")
                    break
        return gitee_repo_name, spec_name, srpm_name
        

    def get_sig_name(self, package):
        """
        get the sig name of specific package
        args:
            pacakge
        returns:
            sig_name: SIG name to which package belongs
        """ 
        sig_name = "NA"
        if not self.sigs_yaml_content:
            return sig_name
        for sig in self.signames:
            for repo in self.sigs_yaml_content[sig]:
                if repo == package:
                    logger.info(f"find {package} in {sig}")
                    return sig
        return sig_name

        
    def get_owner_sigs(self, gitee_id):
        """
        get the sigs maintained by gitee_id

        args:
            gitee_id: Registered gitee ID
        returns:
            own_sigs: Maintained sig list
        """
        own_sigs = []
        for sig_name in self.signames:
            sig_owners = self.get_sig_maintainers(sig_name)
            if gitee_id in sig_owners:
                logger.info(f"{gitee_id} maintain {sig_name}")
                own_sigs.append(sig_name)
        return own_sigs


    def get_sig_maintainers(self, sig_name):
        """
        get maintainers of specific sig

        args:
            sig_name: name of sig
        returns:
            maintainers: maintainers of sig
        """
        maintainers = ["NA"]
        if sig_name is None:
            maintainers = ['NA']
            return maintainers
        
        sigs_OWNERS_yaml_content = self.get_local_sigs_maintainers_yaml(sig_name)
        if not sigs_OWNERS_yaml_content:
            url = constant.SIG_OWNER_URL.format(signame = sig_name)
            resp = http.get(url)
            if resp.status_code != 200:
                logger.warning(f"get sig/owners status_code:{resp.status_code}")
                return maintainers
            owner_yaml_content = resp.text
            if owner_yaml_content is None:
                return maintainers
            else:
                owners = yaml.safe_load(owner_yaml_content)
                return owners.get("maintainers", maintainers)
        else:
            return sigs_OWNERS_yaml_content.get("maintainers", maintainers)

    def get_spec_context(self, package_name, spec_name):
        """
        get spec file content of package
        args:
            package: package name/spec filename
        returns:
            spec_content: content of spec file
        """
        if not spec_name:
            return None
        specurl = constant.SPEC_URL.format( 
            package = package_name, 
            branch = self.branch, 
            specfile = spec_name + ".spec")
        logger.info(f"specurl:{specurl}")
        resp = http.get(specurl)

        if resp is None or resp.status_code != 200:
            logger.warning(f"get spec: None")
            return None
        spec_str = resp.text
        if spec_str is None:
            return None
        else:
            spec = spec_str.splitlines()
        return spec

    def get_latest_contributors(self, package_name, spec_name, max_eamil_num=3):
        """
        get latest contributors's emails

        args:
            package: package name/spec filename
            max_eamil_num: limit the maximum number of eamils
        returns:
            emails: eamils of latest contributors
        """
        spec = self.get_spec_context(package_name, spec_name)
        if spec is None:
            logger.error("get spec of %s failed!", package_name)
            return 'NA'

        emails = self.get_emails_of_contributors(spec, max_eamil_num)
        if emails:
            return emails[0]
        else:
            return 'NA'
        
    def get_emails_of_contributors(self, spec, max_eamil_num):
        """
        analyse the email of contributor in changelog
        args:
            spec: content of spec file
            max_eamil_num: limit the maximum number of eamils
        returns:
            emails: eamils of latest max_eamil_num contributors
        """
        emails = []
        num = 0
        in_changelog = False
        for line in spec:
            if line.startswith("%changelog"):
                in_changelog = True
            if in_changelog and line.startswith("*") and num < max_eamil_num:
                try:
                    regular = re.compile(r'[0-9a-zA-Z\.]+@[0-9a-zA-Z\.]+[com, org]')
                    email = re.findall(regular, line)[0]
                    emails.append(email)
                    num = num + 1
                except IndexError as e:
                    logger.error(f"analyse developer for {line} failed")
                    emails.append(line)

    def get_month_num(self, month):
        """
        """
        month_num_dict={"Jan":"1", "Feb":"2", "Mar":"3", "Apr":"4", "May":"5", "Jun":"6", \
            "July":"7", "Jul":"7", "Aug":"8", "Sept":"9", "Sep":"9", "Oct":"10", "Nov":"11", "Dec":"12"}
        month_number = "NA"
        try:
            month_number = month_num_dict[month]
        except  KeyError as err:
            logger.error(f"{err}: {month} is invalid")
            
        return month_number
        
    def get_initialization_of_pkg(self, package_name, spec_name):
        """
        get the email and date of initialization in changelog
        args:
            spec: content of spec file
        returns:
            email: eamil of initialization
            date: date of initialization
        """
        email = "No developer's email "
        date = "No date"
        MAX_SPLIT_NUM = 7

        spec = self.get_spec_context(package_name, spec_name)
        if spec is None:
            logger.error("get spec of %s failed!", package_name)
            return email, date
        
        for line_num in range(len(spec) - 1, -1, -1):
            line = spec[line_num]
            
            if '*' in line and '@' in line:
                # get email of contributor
                try:
                    regular = re.compile(r'[0-9a-zA-Z\.]+@[0-9a-zA-Z\.]+[com, org]')
                    find_res = re.findall(regular, line)
                    logger.info(f"{find_res}")
                    email = find_res[0]
                except IndexError as e:
                    logger.error(f"analyse developer's email for {line} failed")
                    email = line

                # get date of initialization
                line_list = shlex.split(line)
                if len(line_list) >= MAX_SPLIT_NUM:
                    month = self.get_month_num(line_list[2])
                    day = line_list[3]
                    year = line_list[4]
                    date = f"{year}/{month}/{day}"
                    logger.info(f"{date}")
                    break
            elif '*' in line:
                logger.warning(f"analyse developer's email for {line} failed")
                email = line
                # get date of initialization
                line_list = shlex.split(line)
                if len(line_list) >= MAX_SPLIT_NUM:
                    month = self.get_month_num(line_list[2])
                    day = line_list[3]
                    year = line_list[4]
                    date = f"{year}/{month}/{day}"
                    logger.info(f"{date}")
                    break
                
        logger.info(f"{package_name}: {email}--->{date}")
        return email, date

    def query_sig_repos(self, sig_name=None):
        """
        @description :生成一份gitee上的repo——sig——maintain信息表
        -----------
        @param :
        -----------
        @returns :
        -----------
        """
        def query_sig_repo(sig_name):
            sig_repos = []
            try:
                repos = self.sigs_yaml_content[sig_name]
            except KeyError as ke:
                logger.error(ke)
                return None

            for repo in repos:
                sig_repo = []
                sig_repo.append(repo)
                sig_repo.append(sig_name)

                # sig_maintainers = self.get_sig_maintainers(sig_name)
                sig_maintainers = 'NA'
                if not sig_maintainers:
                    logger.error(f"{sig_name} No sig maintainer found")
                    sig_maintainers = 'NA'
                sig_repo.append(sig_maintainers)
                sig_repos.append(sig_repo)
            return sig_repos

        if sig_name is None:
            logger.info("No sig name is specified")
            res_csv_name = "all_sig_repos.csv"
            with codecs.open(res_csv_name, 'w', 'utf-8') as result_file:
                writer = csv.writer(result_file)
                writer.writerow(["Package", "Sig", "Maintainers"])

                for c_sig_name in self.signames:
                    sig_repos = query_sig_repo(c_sig_name)
                    if sig_repos is None:
                        logger.warning(f"No repos found in {c_sig_name}")
                        return
                    for repo_info in sig_repos:
                        writer.writerow(repo_info)
        else:
            res_csv_name = f"{sig_name}_repos.csv"
            with codecs.open(res_csv_name, 'w', 'utf-8') as result_file:
                writer = csv.writer(result_file)
                writer.writerow(["Package", "Sig", "Maintainers"])
                sig_repos = query_sig_repo(sig_name)
                if sig_repos is None:
                    logger.warning(f"No repos found in {sig_name}")
                    return
                for repo_info in sig_repos:
                    writer.writerow(repo_info)
            

    def is_contain_black_info(self, line):
        """
        """
        black_info = ["Install", "Upgrading", "Last metadata expiration check", "Dependencies resolved", \
            "===", "Version", "Installing dependencies", "Transaction Summary", "Upgrade", \
                "Total download size", "Transaction Summary"]
        for black in black_info:
            if black in line:
                return True

        return False
                    

    def get_depend_src_info(self, pkg_file):
        """
        @description : 获取安装依赖包的源码包信息列表
        -----------
        @param :
        -----------
        @returns :
        -----------
        """
        def get_arrow_num(self, line):
            repo_idx = 0
            line_list = shlex.split(line)
            col_num = len(line_list)
            for key, value in enumerate(line_list):
                if value == "Repository":
                    repo_idx = key
                    break

            return col_num, repo_idx

        pkg_version = dict()
        with open(pkg_file, encoding='utf-8') as f:
            pkg_version = yaml.safe_load(f)

        with codecs.open("FindDependSrcPackages.csv", 'w', 'utf-8') as result_file:
            writer = csv.writer(result_file)
            writer.writerow(["InstallPkg", "Version", "InstallDepPkg", "Reponame", \
                "DependPackageSrc", "InstallDepPkgVersion"])
            for pkg, value in pkg_version.items():
                version = value.get("version")
                logger.info(f"get depended src info of {pkg}-{version}")
                yum_install_cmd = f"yum install {pkg}-{version} --assumeno"
                retcode, cmd_out = subprocess.getstatusoutput(yum_install_cmd)
                
                if  cmd_out is None:
                    logger.error(f"{retcode}: yum install {pkg} failed")
                    continue

                data = cmd_out.split("\n")

                col_num = 6 # 通常情况下是6列
                repo_idx = 3 # 通常情况下是第3列
                i = 0
                in_repository = False
                while i < len(data):
                    srpm_list = []
                    line = data[i]
                    if "Repository" in line:
                        col_num, repo_idx = self.get_arrow_num(line)

                    if not self.is_contain_black_info(line):
                        srpm_list.append(pkg)
                        srpm_list.append(version)
                        info = shlex.split(line)
                        
                        if len(info) < col_num:
                            i = i + 1
                            if i < len(data) and not self.is_contain_black_info(data[i]):
                                info = shlex.split(line + data[i])
                                if len(info) < col_num:
                                    logger.error(f"invalid added info: {info}")
                                    continue
                            else:
                                continue
                        bin_rpm = info[0]
                        srpm_list.append(bin_rpm)
                        bin_repo = info[repo_idx]
                        srpm_list.append(bin_repo)
                        
                        yum_install_cmd = f"yum repoquery --source {bin_rpm} --repo {bin_repo}"

                        retcode, cmd_out = subprocess.getstatusoutput(yum_install_cmd)
                        
                        if  cmd_out is None:
                            logger.error(f"yum install {pkg} failed")
                            continue
                        
                        bin_src=""
                        bin_src_version = ""
                        src_data = cmd_out.split("\n")
                        for src_line in src_data:
                            if 'src.rpm' in src_line and 'oe1' not in src_line:
                                
                                name_ver_rel = src_line.rsplit('.', 2) # 分割成3段
                                ver_rel = name_ver_rel[0].rsplit('-', 2) # 分割成3段
                                bin_src = ver_rel[0]
                                bin_src_version = ver_rel[1]
                                srpm_list.append(bin_src)
                                srpm_list.append(bin_src_version)
                                
                                logger.info(f"{bin_repo}-->{bin_src}-->{bin_src_version}")
                                writer.writerow(srpm_list)
                                break
                    i += 1

    def get_related_email(self, pkg, dev_email=True):
        """
        @description : 获取软件包的sig组信息、最近的贡献者邮箱信息
        -----------
        @param :
        -----------
        @returns :
        -----------
        """
        package_name, spec_name = self.get_package_repo_name(pkg)
        sig_name = self.get_sig_name(package_name)

        if sig_name == "NA":
            logger.warning(f"{package_name} No sig found")

        sig_maintainers = self.get_sig_maintainers(sig_name)

        if dev_email:
            developer_email = self.get_latest_contributors(package_name, spec_name)
            return sig_name, sig_maintainers[0], developer_email

        return sig_name, sig_maintainers[0], 'NA'

    def query_sig_info_of_pkg_list(self, package_list):
        """
        query sig maintainer and latest contributors of package
        """
        pkgs = []
        with open(package_list, encoding='utf-8') as f:
            pkgs = f.readlines()
        
        with codecs.open("Pkg_sig.csv", 'w', 'utf-8') as result_file:
            writer = csv.writer(result_file)
            writer.writerow(["package", "sig", "maintainers"])
            for line in pkgs:
                pkg = line.strip('\n')
                package_name, spec_name = self.get_package_repo_name(pkg)
                logger.info(f"{package_name}")
                
                sig_name = self.get_sig_name(package_name)
                if sig_name == "NA":
                    logger.warning("[%s] No sig found", package_name)

                sig_maintainers = self.get_sig_maintainers(sig_name)
                if not sig_maintainers:
                    logger.warning("[%s] No sig maintainer found", package_name)

                logger.info(f"{package_name}————sig: [{sig_name}] maintainer: {sig_maintainers}")
                pkg_res = [pkg, sig_name, sig_maintainers]
                writer.writerow(pkg_res)

def example_get_init(branch, pkgs_file):

    pkgs = []
    with open(pkgs_file, encoding='utf-8') as f:
        pkgs = f.readlines()
    
    sig = OpenEulerCommunityRepoInfoQuery(branch)
    with codecs.open("Initialization-developer-more.csv", 'w', 'utf-8') as result_file:
        writer = csv.writer(result_file)
        writer.writerow(["package", "email", "date"])
        for line in pkgs:
            pkg = line.strip('\n')
            gitee_repo_name, spec_name, srpm_name = sig.get_gitee_repo_related_name('srpm', pkg)
            email, date = sig.get_initialization_of_pkg(gitee_repo_name, spec_name)
            pkg_res = [pkg, email, date]
            writer.writerow(pkg_res)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    # parser.add_argument("pkg", type=str, default=None, help="The Package to be Queried")
    # parser.add_argument("-pl", "--package_list", default=None, nargs="?", 
    #                     help=" choose the package list form new feature")
    # args = parser.parse_args()
    # if not args.package_list:
    #     logger.error("args invalid, please use -h for help")
    #     quit()
    # example_get_init('openEuler-20.03-LTS-SP3', args.package_list)
    test = OpenEulerCommunityRepoInfoQuery('master')
    # test.query_sig_info_of_pkg_list(args.package_list)
    test.query_sig_repos()




    

    
